﻿using System;
using System.Collections.Generic;
using System.Data.Objects;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace CodeSandbox.Core
{
    internal class ExpressionParser
    {
        private static readonly Type[] PredefinedTypes = new Type[22]
    {
      typeof (object),
      typeof (bool),
      typeof (char),
      typeof (string),
      typeof (sbyte),
      typeof (byte),
      typeof (short),
      typeof (ushort),
      typeof (int),
      typeof (uint),
      typeof (long),
      typeof (ulong),
      typeof (float),
      typeof (double),
      typeof (Decimal),
      typeof (DateTime),
      typeof (DateTimeOffset),
      typeof (TimeSpan),
      typeof (Guid),
      typeof (Math),
      typeof (Convert),
      typeof (EntityFunctions)
    };
        private static readonly Expression TrueLiteral = Expression.Constant(true);
        private static readonly Expression FalseLiteral = Expression.Constant(false);
        private static readonly Expression NullLiteral = Expression.Constant(null);
        private static readonly string KeywordIt = "it";
        private static readonly string KeywordIif = "iif";
        private static readonly string KeywordNew = "new";
        private static readonly string KeywordOuterIt = "outerIt";
        private static Dictionary<string, object> _keywords;
        private readonly Dictionary<string, object> _symbols;
        private IDictionary<string, object> _externals;
        private readonly Dictionary<Expression, string> _literals;
        private ParameterExpression _it;
        private ParameterExpression _outerIt;
        private readonly string _text;
        private int _textPos;
        private readonly int _textLen;
        private char _ch;
        private Token _token;

        public ExpressionParser(ParameterExpression[] parameters, string expression, object[] values)
        {
            if (expression == null)
                throw new ArgumentNullException("expression");
            if (_keywords == null)
                _keywords = CreateKeywords();
            _symbols = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
            _literals = new Dictionary<Expression, string>();
            if (parameters != null)
                ProcessParameters(parameters);
            if (values != null)
                ProcessValues(values);
            _text = expression;
            _textLen = _text.Length;
            SetTextPos(0);
            NextToken();
        }

        private void ProcessParameters(ParameterExpression[] parameters)
        {
            foreach (ParameterExpression parameter in parameters)
            {
                if (!string.IsNullOrEmpty(parameter.Name))
                    AddSymbol(parameter.Name, parameter);
            }
            if (parameters.Length != 1 || !string.IsNullOrEmpty(parameters[0].Name))
                return;
            _it = parameters[0];
        }

        private void ProcessValues(object[] values)
        {
            for (int index = 0; index < values.Length; ++index)
            {
                object obj = values[index];
                if (index == values.Length - 1 && obj is IDictionary<string, object>)
                    _externals = (IDictionary<string, object>)obj;
                else
                    AddSymbol("@" + index.ToString(CultureInfo.InvariantCulture), obj);
            }
        }

        private void AddSymbol(string name, object value)
        {
            if (_symbols.ContainsKey(name))
                throw ParseError("The identifier '{0}' was defined more than once", (object)name);
            _symbols.Add(name, value);
        }

        public Expression Parse(Type resultType)
        {
            int pos = _token.pos;
            Expression expr = ParseExpression();
            if (resultType != null && (expr = PromoteExpression(expr, resultType, true)) == null)
                throw ParseError(pos, "Expression of type '{0}' expected", (object)GetTypeName(resultType));
            ValidateToken(TokenId.End, "Syntax error");
            return expr;
        }

        public IEnumerable<DynamicOrdering> ParseOrdering()
        {
            List<DynamicOrdering> dynamicOrderingList = new List<DynamicOrdering>();
            while (true)
            {
                Expression expression = ParseExpression();
                bool flag = true;
                if (TokenIdentifierIs("asc") || TokenIdentifierIs("ascending"))
                    NextToken();
                else if (TokenIdentifierIs("desc") || TokenIdentifierIs("descending"))
                {
                    NextToken();
                    flag = false;
                }
                dynamicOrderingList.Add(new DynamicOrdering
                {
                    Selector = expression,
                    Ascending = flag
                });
                if (_token.id == TokenId.Comma)
                    NextToken();
                else
                    break;
            }
            ValidateToken(TokenId.End, "Syntax error");
            return dynamicOrderingList;
        }

        private Expression ParseExpression()
        {
            int pos = _token.pos;
            Expression test = ParseLogicalOr();
            if (_token.id == TokenId.Question)
            {
                NextToken();
                Expression expression1 = ParseExpression();
                ValidateToken(TokenId.Colon, "':' expected");
                NextToken();
                Expression expression2 = ParseExpression();
                test = GenerateConditional(test, expression1, expression2, pos);
            }
            return test;
        }

        private Expression ParseLogicalOr()
        {
            Expression left = ParseLogicalAnd();
            while (_token.id == TokenId.DoubleBar || TokenIdentifierIs("or"))
            {
                Token token = _token;
                NextToken();
                Expression logicalAnd = ParseLogicalAnd();
                CheckAndPromoteOperands(typeof(ILogicalSignatures), token.text, ref left, ref logicalAnd, token.pos);
                left = Expression.OrElse(left, logicalAnd);
            }
            return left;
        }

        private Expression ParseLogicalAnd()
        {
            Expression left = ParseComparison();
            while (_token.id == TokenId.DoubleAmphersand || TokenIdentifierIs("and"))
            {
                Token token = _token;
                NextToken();
                Expression comparison = ParseComparison();
                CheckAndPromoteOperands(typeof(ILogicalSignatures), token.text, ref left, ref comparison, token.pos);
                left = Expression.AndAlso(left, comparison);
            }
            return left;
        }

        private Expression ParseComparison()
        {
            Expression left = ParseAdditive();
            while (_token.id == TokenId.Equal || _token.id == TokenId.DoubleEqual || (_token.id == TokenId.ExclamationEqual || _token.id == TokenId.LessGreater) || (_token.id == TokenId.GreaterThan || _token.id == TokenId.GreaterThanEqual || (_token.id == TokenId.LessThan || _token.id == TokenId.LessThanEqual)))
            {
                Token token = _token;
                NextToken();
                Expression right = ParseAdditive();
                bool flag = token.id == TokenId.Equal || token.id == TokenId.DoubleEqual || token.id == TokenId.ExclamationEqual || token.id == TokenId.LessGreater;
                if (flag && !left.Type.IsValueType && !right.Type.IsValueType)
                {
                    if (left.Type != right.Type)
                    {
                        if (left.Type.IsAssignableFrom(right.Type))
                        {
                            right = Expression.Convert(right, left.Type);
                        }
                        else
                        {
                            if (!right.Type.IsAssignableFrom(left.Type))
                                throw IncompatibleOperandsError(token.text, left, right, token.pos);
                            left = Expression.Convert(left, right.Type);
                        }
                    }
                }
                else if (IsEnumType(left.Type) || IsEnumType(right.Type))
                {
                    if (left.Type == right.Type)
                    {
                        Expression expression1;
                        if ((expression1 = PromoteExpression(right, typeof(int), true)) != null)
                            right = expression1;
                        Expression expression2;
                        if ((expression2 = PromoteExpression(left, typeof(int), true)) != null)
                            left = expression2;
                    }
                    else
                    {
                        Expression expression1;
                        if ((expression1 = PromoteExpression(right, left.Type, true)) != null)
                        {
                            right = expression1;
                        }
                        else
                        {
                            Expression expression2;
                            if ((expression2 = PromoteExpression(left, right.Type, true)) == null)
                                throw IncompatibleOperandsError(token.text, left, right, token.pos);
                            left = expression2;
                        }
                    }
                }
                else
                    CheckAndPromoteOperands(flag ? typeof(IEqualitySignatures) : typeof(IRelationalSignatures), token.text, ref left, ref right, token.pos);
                switch (token.id)
                {
                    case TokenId.LessThan:
                        left = GenerateLessThan(left, right);
                        continue;
                    case TokenId.Equal:
                    case TokenId.DoubleEqual:
                        left = GenerateEqual(left, right);
                        continue;
                    case TokenId.GreaterThan:
                        left = GenerateGreaterThan(left, right);
                        continue;
                    case TokenId.ExclamationEqual:
                    case TokenId.LessGreater:
                        left = GenerateNotEqual(left, right);
                        continue;
                    case TokenId.LessThanEqual:
                        left = GenerateLessThanEqual(left, right);
                        continue;
                    case TokenId.GreaterThanEqual:
                        left = GenerateGreaterThanEqual(left, right);
                        continue;
                    default:
                        continue;
                }
            }
            return left;
        }

        private Expression ParseAdditive()
        {
            Expression left = ParseMultiplicative();
            while (_token.id == TokenId.Plus || _token.id == TokenId.Minus || _token.id == TokenId.Amphersand)
            {
                Token token = _token;
                NextToken();
                Expression multiplicative = ParseMultiplicative();
                switch (token.id)
                {
                    case TokenId.Amphersand:
                        left = GenerateStringConcat(left, multiplicative);
                        continue;
                    case TokenId.Plus:
                        if (!(left.Type == typeof(string)) && !(multiplicative.Type == typeof(string)))
                        {
                            CheckAndPromoteOperands(typeof(IAddSignatures), token.text, ref left, ref multiplicative, token.pos);
                            left = GenerateAdd(left, multiplicative);
                            continue;
                        }
                        goto case TokenId.Amphersand;
                    case TokenId.Minus:
                        CheckAndPromoteOperands(typeof(ISubtractSignatures), token.text, ref left, ref multiplicative, token.pos);
                        left = GenerateSubtract(left, multiplicative);
                        continue;
                    default:
                        continue;
                }
            }
            return left;
        }

        private Expression ParseMultiplicative()
        {
            Expression left = ParseUnary();
            while (_token.id == TokenId.Asterisk || _token.id == TokenId.Slash || (_token.id == TokenId.Percent || TokenIdentifierIs("mod")))
            {
                Token token = _token;
                NextToken();
                Expression unary = ParseUnary();
                CheckAndPromoteOperands(typeof(IArithmeticSignatures), token.text, ref left, ref unary, token.pos);
                switch (token.id)
                {
                    case TokenId.Asterisk:
                        left = Expression.Multiply(left, unary);
                        continue;
                    case TokenId.Slash:
                        left = Expression.Divide(left, unary);
                        continue;
                    case TokenId.Identifier:
                    case TokenId.Percent:
                        left = Expression.Modulo(left, unary);
                        continue;
                    default:
                        continue;
                }
            }
            return left;
        }

        private Expression ParseUnary()
        {
            if (_token.id != TokenId.Minus && _token.id != TokenId.Exclamation && !TokenIdentifierIs("not"))
                return ParsePrimary();
            Token token = _token;
            NextToken();
            if (token.id == TokenId.Minus && (_token.id == TokenId.IntegerLiteral || _token.id == TokenId.RealLiteral))
            {
                _token.text = "-" + _token.text;
                _token.pos = token.pos;
                return ParsePrimary();
            }
            Expression unary = ParseUnary();
            Expression expression;
            if (token.id == TokenId.Minus)
            {
                CheckAndPromoteOperand(typeof(INegationSignatures), token.text, ref unary, token.pos);
                expression = Expression.Negate(unary);
            }
            else
            {
                CheckAndPromoteOperand(typeof(INotSignatures), token.text, ref unary, token.pos);
                expression = Expression.Not(unary);
            }
            return expression;
        }

        private Expression ParsePrimary()
        {
            Expression expression = ParsePrimaryStart();
            while (true)
            {
                while (_token.id != TokenId.Dot)
                {
                    if (_token.id != TokenId.OpenBracket)
                        return expression;
                    expression = ParseElementAccess(expression);
                }
                NextToken();
                expression = ParseMemberAccess(null, expression);
            }
        }

        private Expression ParsePrimaryStart()
        {
            switch (_token.id)
            {
                case TokenId.Identifier:
                    return ParseIdentifier();
                case TokenId.StringLiteral:
                    return ParseStringLiteral();
                case TokenId.IntegerLiteral:
                    return ParseIntegerLiteral();
                case TokenId.RealLiteral:
                    return ParseRealLiteral();
                case TokenId.OpenParen:
                    return ParseParenExpression();
                default:
                    throw ParseError("Expression expected");
            }
        }

        private Expression ParseStringLiteral()
        {
            ValidateToken(TokenId.StringLiteral);
            char ch = _token.text[0];
            string text = _token.text.Substring(1, _token.text.Length - 2);
            int startIndex1 = 0;
            while (true)
            {
                int startIndex2 = text.IndexOf(ch, startIndex1);
                if (startIndex2 >= 0)
                {
                    text = text.Remove(startIndex2, 1);
                    startIndex1 = startIndex2 + 1;
                }
                else
                    break;
            }
            if (ch == 39)
            {
                if (text.Length != 1)
                    throw ParseError("Character literal must contain exactly one character");
                NextToken();
                return CreateLiteral(text[0], text);
            }
            NextToken();
            return CreateLiteral(text, text);
        }

        private Expression ParseIntegerLiteral()
        {
            ValidateToken(TokenId.IntegerLiteral);
            string text = _token.text;
            if (text[0] != 45)
            {
                ulong result;
                if (!ulong.TryParse(text, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out result))
                    throw ParseError("Invalid integer literal '{0}'", (object)text);
                NextToken();
                if (result <= int.MaxValue)
                    return CreateLiteral((int)result, text);
                if (result <= uint.MaxValue)
                    return CreateLiteral((uint)result, text);
                if (result <= 9223372036854775807UL)
                    return CreateLiteral((long)result, text);
                return CreateLiteral(result, text);
            }
            long result1;
            if (!long.TryParse(text, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out result1))
                throw ParseError("Invalid integer literal '{0}'", (object)text);
            NextToken();
            if (result1 >= int.MinValue && result1 <= int.MaxValue)
                return CreateLiteral((int)result1, text);
            return CreateLiteral(result1, text);
        }

        private Expression ParseRealLiteral()
        {
            ValidateToken(TokenId.RealLiteral);
            string text = _token.text;
            object obj = null;
            switch (text[text.Length - 1])
            {
                case 'F':
                case 'f':
                    float result1;
                    if (float.TryParse(text.Substring(0, text.Length - 1), NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.InvariantInfo, out result1))
                    {
                        obj = result1;
                    }
                    break;
                default:
                    double result2;
                    if (double.TryParse(text, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.InvariantInfo, out result2))
                    {
                        obj = result2;
                    }
                    break;
            }
            if (obj == null)
                throw ParseError("Invalid real literal '{0}'", (object)text);
            NextToken();
            return CreateLiteral(obj, text);
        }

        private Expression CreateLiteral(object value, string text)
        {
            ConstantExpression constantExpression = Expression.Constant(value);
            _literals.Add(constantExpression, text);
            return constantExpression;
        }

        private Expression ParseParenExpression()
        {
            ValidateToken(TokenId.OpenParen, "'(' expected");
            NextToken();
            Expression expression = ParseExpression();
            ValidateToken(TokenId.CloseParen, "')' or operator expected");
            NextToken();
            return expression;
        }

        private Expression ParseIdentifier()
        {
            ValidateToken(TokenId.Identifier);
            object obj;
            if (_keywords.TryGetValue(_token.text, out obj))
            {
                if ((object)(obj as Type) != null)
                    return ParseTypeAccess((Type)obj);
                if (obj == KeywordIt)
                    return ParseIt();
                if (obj == KeywordOuterIt)
                    return ParseOuterIt();
                if (obj == KeywordIif)
                    return ParseIif();
                if (obj == KeywordNew)
                    return ParseNew();
                NextToken();
                return (Expression)obj;
            }
            if (_symbols.TryGetValue(_token.text, out obj) || _externals != null && _externals.TryGetValue(_token.text, out obj))
            {
                Expression expression = obj as Expression;
                if (expression == null)
                {
                    expression = Expression.Constant(obj);
                }
                else
                {
                    LambdaExpression lambda = expression as LambdaExpression;
                    if (lambda != null)
                        return ParseLambdaInvocation(lambda);
                }
                NextToken();
                return expression;
            }
            if (_it != null)
                return ParseMemberAccess(null, _it);
            throw ParseError("Unknown identifier '{0}'", (object)_token.text);
        }

        private Expression ParseIt()
        {
            if (_it == null)
                throw ParseError("No 'it' is in scope");
            NextToken();
            return _it;
        }

        private Expression ParseOuterIt()
        {
            if (_outerIt == null)
                throw ParseError("No 'it' is in scope");
            NextToken();
            return _outerIt;
        }

        private Expression ParseIif()
        {
            int pos = _token.pos;
            NextToken();
            Expression[] argumentList = ParseArgumentList();
            if (argumentList.Length != 3)
                throw ParseError(pos, "The 'iif' function requires three arguments");
            return GenerateConditional(argumentList[0], argumentList[1], argumentList[2], pos);
        }

        private Expression GenerateConditional(Expression test, Expression expr1, Expression expr2, int errorPos)
        {
            if (test.Type != typeof(bool))
                throw ParseError(errorPos, "The first expression must be of type 'Boolean'");
            if (expr1.Type != expr2.Type)
            {
                Expression expression1 = expr2 != NullLiteral ? PromoteExpression(expr1, expr2.Type, true) : null;
                Expression expression2 = expr1 != NullLiteral ? PromoteExpression(expr2, expr1.Type, true) : null;
                if (expression1 != null && expression2 == null)
                    expr1 = expression1;
                else if (expression2 != null && expression1 == null)
                {
                    expr2 = expression2;
                }
                else
                {
                    string str1 = expr1 != NullLiteral ? expr1.Type.Name : "null";
                    string str2 = expr2 != NullLiteral ? expr2.Type.Name : "null";
                    if (expression1 != null && expression2 != null)
                        throw ParseError(errorPos, "Both of the types '{0}' and '{1}' convert to the other", (object)str1, (object)str2);
                    throw ParseError(errorPos, "Neither of the types '{0}' and '{1}' converts to the other", (object)str1, (object)str2);
                }
            }
            return Expression.Condition(test, expr1, expr2);
        }

        private Expression ParseNew()
        {
            NextToken();
            ValidateToken(TokenId.OpenParen, "'(' expected");
            NextToken();
            List<DynamicProperty> dynamicPropertyList = new List<DynamicProperty>();
            List<Expression> expressionList = new List<Expression>();
            int pos;
            while (true)
            {
                pos = _token.pos;
                Expression expression = ParseExpression();
                string name;
                if (TokenIdentifierIs("as"))
                {
                    NextToken();
                    name = GetIdentifier();
                    NextToken();
                }
                else
                {
                    MemberExpression memberExpression = expression as MemberExpression;
                    if (memberExpression != null)
                        name = memberExpression.Member.Name;
                    else
                        break;
                }
                expressionList.Add(expression);
                dynamicPropertyList.Add(new DynamicProperty(name, expression.Type));
                if (_token.id == TokenId.Comma)
                    NextToken();
                else
                    goto label_8;
            }
            throw ParseError(pos, "Expression is missing an 'as' clause");
        label_8:
            ValidateToken(TokenId.CloseParen, "')' or ',' expected");
            NextToken();
            Type type = DynamicCodeExpression.CreateClass((IEnumerable<DynamicProperty>)dynamicPropertyList);
            MemberBinding[] memberBindingArray = new MemberBinding[dynamicPropertyList.Count];
            for (int index = 0; index < memberBindingArray.Length; ++index)
                memberBindingArray[index] = Expression.Bind(type.GetProperty(dynamicPropertyList[index].Name), expressionList[index]);
            return Expression.MemberInit(Expression.New(type), memberBindingArray);
        }

        private Expression ParseLambdaInvocation(LambdaExpression lambda)
        {
            int pos = _token.pos;
            NextToken();
            Expression[] argumentList = ParseArgumentList();
            MethodBase method;
            if (FindMethod(lambda.Type, "Invoke", false, argumentList, out method) != 1)
                throw ParseError(pos, "Argument list incompatible with lambda expression");
            return Expression.Invoke(lambda, argumentList);
        }

        private Expression ParseTypeAccess(Type type)
        {
            int pos = _token.pos;
            NextToken();
            if (_token.id == TokenId.Question)
            {
                if (!type.IsValueType || IsNullableType(type))
                    throw ParseError(pos, "Type '{0}' has no nullable form", (object)GetTypeName(type));
                type = typeof(Nullable<>).MakeGenericType(type);
                NextToken();
            }
            if (_token.id == TokenId.OpenParen)
            {
                Expression[] argumentList = ParseArgumentList();
                MethodBase method;
                switch (FindBestMethod(type.GetConstructors(), argumentList, out method))
                {
                    case 0:
                        if (argumentList.Length == 1)
                            return GenerateConversion(argumentList[0], type, pos);
                        throw ParseError(pos, "No matching constructor in type '{0}'", (object)GetTypeName(type));
                    case 1:
                        return Expression.New((ConstructorInfo)method, argumentList);
                    default:
                        throw ParseError(pos, "Ambiguous invocation of '{0}' constructor", (object)GetTypeName(type));
                }
            }
            ValidateToken(TokenId.Dot, "'.' or '(' expected");
            NextToken();
            return ParseMemberAccess(type, null);
        }

        private Expression GenerateConversion(Expression expr, Type type, int errorPos)
        {
            Type type1 = expr.Type;
            if (type1 == type)
                return expr;
            if (type1.IsValueType && type.IsValueType)
            {
                if ((IsNullableType(type1) || IsNullableType(type)) && GetNonNullableType(type1) == GetNonNullableType(type))
                    return Expression.Convert(expr, type);
                if ((IsNumericType(type1) || IsEnumType(type1)) && IsNumericType(type) || IsEnumType(type))
                    return Expression.ConvertChecked(expr, type);
            }
            if (type1.IsAssignableFrom(type) || type.IsAssignableFrom(type1) || (type1.IsInterface || type.IsInterface))
                return Expression.Convert(expr, type);
            throw ParseError(errorPos, "A value of type '{0}' cannot be converted to type '{1}'", (object)GetTypeName(type1), (object)GetTypeName(type));
        }

        private Expression ParseMemberAccess(Type type, Expression instance)
        {
            if (instance != null)
                type = instance.Type;
            int pos = _token.pos;
            string identifier = GetIdentifier();
            NextToken();
            if (_token.id == TokenId.OpenParen)
            {
                if (instance != null && type != typeof(string))
                {
                    Type genericType = FindGenericType(typeof(IEnumerable<>), type);
                    if (genericType != null)
                    {
                        Type genericArgument = genericType.GetGenericArguments()[0];
                        return ParseAggregate(instance, genericArgument, identifier, pos);
                    }
                }
                Expression[] argumentList = ParseArgumentList();
                MethodBase method1;
                switch (FindMethod(type, identifier, instance == null, argumentList, out method1))
                {
                    case 0:
                        throw ParseError(pos, "No applicable method '{0}' exists in type '{1}'", (object)identifier, (object)GetTypeName(type));
                    case 1:
                        MethodInfo method2 = (MethodInfo)method1;
                        if (method2.ReturnType == typeof(void))
                            throw ParseError(pos, "Method '{0}' in type '{1}' does not return a value", (object)identifier, (object)GetTypeName(method2.DeclaringType));
                        return Expression.Call(instance, method2, argumentList);
                    default:
                        throw ParseError(pos, "Ambiguous invocation of method '{0}' in type '{1}'", (object)identifier, (object)GetTypeName(type));
                }
            }
            MemberInfo propertyOrField = FindPropertyOrField(type, identifier, instance == null);
            if (propertyOrField == null)
                throw ParseError(pos, "No property or field '{0}' exists in type '{1}'", (object)identifier, (object)GetTypeName(type));
            if ((object)(propertyOrField as PropertyInfo) == null)
                return Expression.Field(instance, (FieldInfo)propertyOrField);
            return Expression.Property(instance, (PropertyInfo)propertyOrField);
        }

        private static Type FindGenericType(Type generic, Type type)
        {
            for (; type != (Type)null && type != typeof(object); type = type.BaseType)
            {
                if (type.IsGenericType && type.GetGenericTypeDefinition() == generic)
                    return type;
                if (generic.IsInterface)
                {
                    foreach (Type type1 in type.GetInterfaces())
                    {
                        Type genericType = FindGenericType(generic, type1);
                        if (genericType != null)
                            return genericType;
                    }
                }
            }
            return null;
        }

        private Expression ParseAggregate(Expression instance, Type elementType, string methodName, int errorPos)
        {
            _outerIt = _it;
            ParameterExpression parameterExpression = Expression.Parameter(elementType, "");
            _it = parameterExpression;
            Expression[] argumentList = ParseArgumentList();
            _it = _outerIt;
            MethodBase method;
            if (FindMethod(typeof(IEnumerableSignatures), methodName, false, argumentList, out method) != 1)
                throw ParseError(errorPos, "No applicable aggregate method '{0}' exists", (object)methodName);
            Type[] typeArguments;
            if (method.Name == "Min" || method.Name == "Max")
                typeArguments = new Type[2]
        {
          elementType,
          argumentList[0].Type
        };
            else
                typeArguments = new Type[1] { elementType };
            Expression[] expressionArray;
            if (argumentList.Length == 0)
                expressionArray = new Expression[1] { instance };
            else if (method.Name == "Contains")
                expressionArray = new Expression[2]
        {
          instance,
          argumentList[0]
        };
            else
                expressionArray = new Expression[2]
        {
          instance,
          Expression.Lambda(argumentList[0], parameterExpression)
        };
            return Expression.Call(typeof(Enumerable), method.Name, typeArguments, expressionArray);
        }

        private Expression[] ParseArgumentList()
        {
            ValidateToken(TokenId.OpenParen, "'(' expected");
            NextToken();
            Expression[] expressionArray = _token.id != TokenId.CloseParen ? ParseArguments() : new Expression[0];
            ValidateToken(TokenId.CloseParen, "')' or ',' expected");
            NextToken();
            return expressionArray;
        }

        private Expression[] ParseArguments()
        {
            List<Expression> expressionList = new List<Expression>();
            while (true)
            {
                expressionList.Add(ParseExpression());
                if (_token.id == TokenId.Comma)
                    NextToken();
                else
                    break;
            }
            return expressionList.ToArray();
        }

        private Expression ParseElementAccess(Expression expr)
        {
            int pos = _token.pos;
            ValidateToken(TokenId.OpenBracket, "'(' expected");
            NextToken();
            Expression[] arguments = ParseArguments();
            ValidateToken(TokenId.CloseBracket, "']' or ',' expected");
            NextToken();
            if (expr.Type.IsArray)
            {
                if (expr.Type.GetArrayRank() != 1 || arguments.Length != 1)
                    throw ParseError(pos, "Indexing of multi-dimensional arrays is not supported");
                Expression index = PromoteExpression(arguments[0], typeof(int), true);
                if (index == null)
                    throw ParseError(pos, "Array index must be an integer expression");
                return Expression.ArrayIndex(expr, index);
            }
            MethodBase method;
            switch (FindIndexer(expr.Type, arguments, out method))
            {
                case 0:
                    throw ParseError(pos, "No applicable indexer exists in type '{0}'", (object)GetTypeName(expr.Type));
                case 1:
                    return Expression.Call(expr, (MethodInfo)method, arguments);
                default:
                    throw ParseError(pos, "Ambiguous invocation of indexer in type '{0}'", (object)GetTypeName(expr.Type));
            }
        }

        private static bool IsNullableType(Type type)
        {
            if (type.IsGenericType)
                return type.GetGenericTypeDefinition() == typeof(Nullable<>);
            return false;
        }

        private static Type GetNonNullableType(Type type)
        {
            if (!IsNullableType(type))
                return type;
            return type.GetGenericArguments()[0];
        }

        private static string GetTypeName(Type type)
        {
            Type nonNullableType = GetNonNullableType(type);
            string name = nonNullableType.Name;
            if (type != nonNullableType)
                name += (string)(object)'?';
            return name;
        }

        private static bool IsNumericType(Type type)
        {
            return GetNumericTypeKind(type) != 0;
        }

        private static bool IsSignedIntegralType(Type type)
        {
            return GetNumericTypeKind(type) == 2;
        }

        private static bool IsUnsignedIntegralType(Type type)
        {
            return GetNumericTypeKind(type) == 3;
        }

        private static int GetNumericTypeKind(Type type)
        {
            type = GetNonNullableType(type);
            if (type.IsEnum)
                return 0;
            switch (Type.GetTypeCode(type))
            {
                case TypeCode.Char:
                case TypeCode.Single:
                case TypeCode.Double:
                case TypeCode.Decimal:
                    return 1;
                case TypeCode.SByte:
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                    return 2;
                case TypeCode.Byte:
                case TypeCode.UInt16:
                case TypeCode.UInt32:
                case TypeCode.UInt64:
                    return 3;
                default:
                    return 0;
            }
        }

        private static bool IsEnumType(Type type)
        {
            return GetNonNullableType(type).IsEnum;
        }

        private void CheckAndPromoteOperand(Type signatures, string opName, ref Expression expr, int errorPos)
        {
            Expression[] args = new Expression[1] { expr };
            MethodBase method;
            if (FindMethod(signatures, "F", false, args, out method) != 1)
                throw ParseError(errorPos, "Operator '{0}' incompatible with operand type '{1}'", (object)opName, (object)GetTypeName(args[0].Type));
            expr = args[0];
        }

        private void CheckAndPromoteOperands(Type signatures, string opName, ref Expression left, ref Expression right, int errorPos)
        {
            Expression[] args = new Expression[2]
      {
        left,
        right
      };
            MethodBase method;
            if (FindMethod(signatures, "F", false, args, out method) != 1)
                throw IncompatibleOperandsError(opName, left, right, errorPos);
            left = args[0];
            right = args[1];
        }

        private Exception IncompatibleOperandsError(string opName, Expression left, Expression right, int pos)
        {
            return ParseError(pos, "Operator '{0}' incompatible with operand types '{1}' and '{2}'", (object)opName, (object)GetTypeName(left.Type), (object)GetTypeName(right.Type));
        }

        private MemberInfo FindPropertyOrField(Type type, string memberName, bool staticAccess)
        {
            BindingFlags bindingAttr = (BindingFlags)(18 | (staticAccess ? 8 : 4));
            foreach (Type selfAndBaseType in SelfAndBaseTypes(type))
            {
                MemberInfo[] members = selfAndBaseType.FindMembers(MemberTypes.Field | MemberTypes.Property, bindingAttr, Type.FilterNameIgnoreCase, memberName);
                if (members.Length != 0)
                    return members[0];
            }
            return null;
        }

        private int FindMethod(Type type, string methodName, bool staticAccess, Expression[] args, out MethodBase method)
        {
            BindingFlags bindingAttr = (BindingFlags)(18 | (staticAccess ? 8 : 4));
            foreach (Type selfAndBaseType in SelfAndBaseTypes(type))
            {
                int bestMethod = FindBestMethod(selfAndBaseType.FindMembers(MemberTypes.Method, bindingAttr, Type.FilterNameIgnoreCase, methodName).Cast<MethodBase>(), args, out method);
                if (bestMethod != 0)
                    return bestMethod;
            }
            method = null;
            return 0;
        }

        private int FindIndexer(Type type, Expression[] args, out MethodBase method)
        {
            foreach (Type selfAndBaseType in SelfAndBaseTypes(type))
            {
                MemberInfo[] defaultMembers = selfAndBaseType.GetDefaultMembers();
                if (defaultMembers.Length != 0)
                {
                    int bestMethod = FindBestMethod(defaultMembers.OfType<PropertyInfo>().Select(p => (MethodBase)p.GetGetMethod()).Where(m => m != (MethodBase)null), args, out method);
                    if (bestMethod != 0)
                        return bestMethod;
                }
            }
            method = null;
            return 0;
        }

        private static IEnumerable<Type> SelfAndBaseTypes(Type type)
        {
            if (!type.IsInterface)
                return SelfAndBaseClasses(type);
            List<Type> types = new List<Type>();
            AddInterface(types, type);
            return types;
        }

        private static IEnumerable<Type> SelfAndBaseClasses(Type type)
        {
            for (; type != (Type)null; type = type.BaseType)
                yield return type;
        }

        private static void AddInterface(List<Type> types, Type type)
        {
            if (types.Contains(type))
                return;
            types.Add(type);
            foreach (Type type1 in type.GetInterfaces())
                AddInterface(types, type1);
        }

        private int FindBestMethod(IEnumerable<MethodBase> methods, Expression[] args, out MethodBase method)
        {
            MethodData[] applicable = methods.Select(m => new MethodData
            {
                MethodBase = m,
                Parameters = m.GetParameters()
            }).Where(m => IsApplicable(m, args)).ToArray();
            if (applicable.Length > 1)
                applicable = applicable.Where(m => applicable.All(n =>
                {
                    if (m != n)
                        return IsBetterThan(args, m, n);
                    return true;
                })).ToArray();
            if (applicable.Length == 1)
            {
                MethodData methodData = applicable[0];
                for (int index = 0; index < args.Length; ++index)
                    args[index] = methodData.Args[index];
                method = methodData.MethodBase;
            }
            else
                method = null;
            return applicable.Length;
        }

        private bool IsApplicable(MethodData method, Expression[] args)
        {
            if (method.Parameters.Length != args.Length)
                return false;
            Expression[] expressionArray = new Expression[args.Length];
            for (int index = 0; index < args.Length; ++index)
            {
                ParameterInfo parameter = method.Parameters[index];
                if (parameter.IsOut)
                    return false;
                Expression expression = PromoteExpression(args[index], parameter.ParameterType, false);
                if (expression == null)
                    return false;
                expressionArray[index] = expression;
            }
            method.Args = expressionArray;
            return true;
        }

        private Expression PromoteExpression(Expression expr, Type type, bool exact)
        {
            if (expr.Type == type)
                return expr;
            if (expr is ConstantExpression)
            {
                ConstantExpression constantExpression = (ConstantExpression)expr;
                if (constantExpression == NullLiteral)
                {
                    if (!type.IsValueType || IsNullableType(type))
                        return Expression.Constant(null, type);
                }
                else
                {
                    string str;
                    if (_literals.TryGetValue(constantExpression, out str))
                    {
                        Type nonNullableType = GetNonNullableType(type);
                        object obj = null;
                        switch (Type.GetTypeCode(constantExpression.Type))
                        {
                            case TypeCode.Int32:
                            case TypeCode.UInt32:
                            case TypeCode.Int64:
                            case TypeCode.UInt64:
                                obj = ParseNumber(str, nonNullableType);
                                break;
                            case TypeCode.Double:
                                if (nonNullableType == typeof(Decimal))
                                {
                                    obj = ParseNumber(str, nonNullableType);
                                }
                                break;
                            case TypeCode.String:
                                obj = ParseEnum(str, nonNullableType);
                                break;
                        }
                        if (obj != null)
                            return Expression.Constant(obj, type);
                    }
                }
            }
            if (!IsCompatibleWith(expr.Type, type))
                return null;
            if (type.IsValueType || exact)
                return Expression.Convert(expr, type);
            return expr;
        }

        private static object ParseNumber(string text, Type type)
        {
            switch (Type.GetTypeCode(GetNonNullableType(type)))
            {
                case TypeCode.SByte:
                    sbyte result1;
                    if (sbyte.TryParse(text, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out result1))
                        return result1;
                    break;
                case TypeCode.Byte:
                    byte result2;
                    if (byte.TryParse(text, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out result2))
                        return result2;
                    break;
                case TypeCode.Int16:
                    short result3;
                    if (short.TryParse(text, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out result3))
                        return result3;
                    break;
                case TypeCode.UInt16:
                    ushort result4;
                    if (ushort.TryParse(text, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out result4))
                        return result4;
                    break;
                case TypeCode.Int32:
                    int result5;
                    if (int.TryParse(text, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out result5))
                        return result5;
                    break;
                case TypeCode.UInt32:
                    uint result6;
                    if (uint.TryParse(text, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out result6))
                        return result6;
                    break;
                case TypeCode.Int64:
                    long result7;
                    if (long.TryParse(text, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out result7))
                        return result7;
                    break;
                case TypeCode.UInt64:
                    ulong result8;
                    if (ulong.TryParse(text, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out result8))
                        return result8;
                    break;
                case TypeCode.Single:
                    float result9;
                    if (float.TryParse(text, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.InvariantInfo, out result9))
                        return result9;
                    break;
                case TypeCode.Double:
                    double result10;
                    if (double.TryParse(text, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.InvariantInfo, out result10))
                        return result10;
                    break;
                case TypeCode.Decimal:
                    Decimal result11;
                    if (Decimal.TryParse(text, NumberStyles.Number, NumberFormatInfo.InvariantInfo, out result11))
                        return result11;
                    break;
            }
            return null;
        }

        private static object ParseEnum(string name, Type type)
        {
            if (type.IsEnum)
            {
                MemberInfo[] members = type.FindMembers(MemberTypes.Field, BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public, Type.FilterNameIgnoreCase, name);
                if (members.Length != 0)
                    return ((FieldInfo)members[0]).GetValue(null);
            }
            return null;
        }

        private static bool IsCompatibleWith(Type source, Type target)
        {
            if (source == target)
                return true;
            if (!target.IsValueType)
                return target.IsAssignableFrom(source);
            Type nonNullableType1 = GetNonNullableType(source);
            Type nonNullableType2 = GetNonNullableType(target);
            if (nonNullableType1 != source && nonNullableType2 == target)
                return false;
            TypeCode typeCode1 = nonNullableType1.IsEnum ? TypeCode.Object : Type.GetTypeCode(nonNullableType1);
            TypeCode typeCode2 = nonNullableType2.IsEnum ? TypeCode.Object : Type.GetTypeCode(nonNullableType2);
            if (nonNullableType1.IsEnum & !nonNullableType2.IsEnum)
            {
                switch (typeCode2)
                {
                    case TypeCode.Int16:
                    case TypeCode.Int32:
                    case TypeCode.Int64:
                        return true;
                }
            }
            switch (typeCode1)
            {
                case TypeCode.SByte:
                {
                    switch (typeCode2)
                    {
                        case TypeCode.SByte:
                        case TypeCode.Int16:
                        case TypeCode.Int32:
                        case TypeCode.Int64:
                        case TypeCode.Single:
                        case TypeCode.Double:
                        case TypeCode.Decimal:
                            return true;
                    }
                    break;
                }
                case TypeCode.Byte:
                {
                    switch (typeCode2)
                    {
                        case TypeCode.Byte:
                        case TypeCode.Int16:
                        case TypeCode.UInt16:
                        case TypeCode.Int32:
                        case TypeCode.UInt32:
                        case TypeCode.Int64:
                        case TypeCode.UInt64:
                        case TypeCode.Single:
                        case TypeCode.Double:
                        case TypeCode.Decimal:
                            return true;
                    }
                    break;
                }
                case TypeCode.Int16:
                {
                    switch (typeCode2)
                    {
                        case TypeCode.Int16:
                        case TypeCode.Int32:
                        case TypeCode.Int64:
                        case TypeCode.Single:
                        case TypeCode.Double:
                        case TypeCode.Decimal:
                            return true;
                    }
                    break;
                }
                case TypeCode.UInt16:
                {
                    switch (typeCode2)
                    {
                        case TypeCode.UInt16:
                        case TypeCode.Int32:
                        case TypeCode.UInt32:
                        case TypeCode.Int64:
                        case TypeCode.UInt64:
                        case TypeCode.Single:
                        case TypeCode.Double:
                        case TypeCode.Decimal:
                            return true;
                    }
                    break;
                }
                case TypeCode.Int32:
                {
                    switch (typeCode2)
                    {
                        case TypeCode.Int32:
                        case TypeCode.Int64:
                        case TypeCode.Single:
                        case TypeCode.Double:
                        case TypeCode.Decimal:
                            return true;
                    }
                    break;
                }
                case TypeCode.UInt32:
                {
                    switch (typeCode2)
                    {
                        case TypeCode.UInt32:
                        case TypeCode.Int64:
                        case TypeCode.UInt64:
                        case TypeCode.Single:
                        case TypeCode.Double:
                        case TypeCode.Decimal:
                            return true;
                    }
                    break;
                }
                case TypeCode.Int64:
                {
                    switch (typeCode2)
                    {
                        case TypeCode.Int64:
                        case TypeCode.Single:
                        case TypeCode.Double:
                        case TypeCode.Decimal:
                            return true;
                    }
                    break;
                }
                case TypeCode.UInt64:
                {
                    switch (typeCode2)
                    {
                        case TypeCode.UInt64:
                        case TypeCode.Single:
                        case TypeCode.Double:
                        case TypeCode.Decimal:
                            return true;
                    }
                    break;
                }
                case TypeCode.Single:
                {
                    switch (typeCode2)
                    {
                        case TypeCode.Single:
                        case TypeCode.Double:
                            return true;
                    }
                    break;
                }
                default:
                {
                    if (nonNullableType1 == nonNullableType2)
                        return true;
                    break;
                }
            }
            return false;
        }

        private static bool IsBetterThan(Expression[] args, MethodData m1, MethodData m2)
        {
            bool flag = false;
            for (int index = 0; index < args.Length; ++index)
            {
                int num = CompareConversions(args[index].Type, m1.Parameters[index].ParameterType, m2.Parameters[index].ParameterType);
                if (num < 0)
                    return false;
                if (num > 0)
                    flag = true;
            }
            return flag;
        }

        private static int CompareConversions(Type s, Type t1, Type t2)
        {
            if (t1 == t2)
                return 0;
            if (s == t1)
                return 1;
            if (s == t2)
                return -1;
            bool flag1 = IsCompatibleWith(t1, t2);
            bool flag2 = IsCompatibleWith(t2, t1);
            if (flag1 && !flag2)
                return 1;
            if (flag2 && !flag1)
                return -1;
            if (IsSignedIntegralType(t1) && IsUnsignedIntegralType(t2))
                return 1;
            return IsSignedIntegralType(t2) && IsUnsignedIntegralType(t1) ? -1 : 0;
        }

        private Expression GenerateEqual(Expression left, Expression right)
        {
            return Expression.Equal(left, right);
        }

        private Expression GenerateNotEqual(Expression left, Expression right)
        {
            return Expression.NotEqual(left, right);
        }

        private Expression GenerateGreaterThan(Expression left, Expression right)
        {
            if (left.Type == typeof(string))
                return Expression.GreaterThan(GenerateStaticMethodCall("Compare", left, right), Expression.Constant(0));
            return Expression.GreaterThan(left, right);
        }

        private Expression GenerateGreaterThanEqual(Expression left, Expression right)
        {
            if (left.Type == typeof(string))
                return Expression.GreaterThanOrEqual(GenerateStaticMethodCall("Compare", left, right), Expression.Constant(0));
            return Expression.GreaterThanOrEqual(left, right);
        }

        private Expression GenerateLessThan(Expression left, Expression right)
        {
            if (left.Type == typeof(string))
                return Expression.LessThan(GenerateStaticMethodCall("Compare", left, right), Expression.Constant(0));
            return Expression.LessThan(left, right);
        }

        private Expression GenerateLessThanEqual(Expression left, Expression right)
        {
            if (left.Type == typeof(string))
                return Expression.LessThanOrEqual(GenerateStaticMethodCall("Compare", left, right), Expression.Constant(0));
            return Expression.LessThanOrEqual(left, right);
        }

        private Expression GenerateAdd(Expression left, Expression right)
        {
            if (left.Type == typeof(string) && right.Type == typeof(string))
                return GenerateStaticMethodCall("Concat", left, right);
            return Expression.Add(left, right);
        }

        private Expression GenerateSubtract(Expression left, Expression right)
        {
            return Expression.Subtract(left, right);
        }

        private Expression GenerateStringConcat(Expression left, Expression right)
        {
            return Expression.Call(null, typeof(string).GetMethod("Concat", new Type[2]
            {
                typeof (object),
                typeof (object)
            }), new Expression[2]
            {
                left,
                right
            });
        }

        private MethodInfo GetStaticMethod(string methodName, Expression left, Expression right)
        {
            return left.Type.GetMethod(methodName, new Type[2]
      {
        left.Type,
        right.Type
      });
        }

        private Expression GenerateStaticMethodCall(string methodName, Expression left, Expression right)
        {
            return Expression.Call(null, GetStaticMethod(methodName, left, right), new Expression[2]
            {
                left,
                right
            });
        }

        private void SetTextPos(int pos)
        {
            _textPos = pos;
            _ch = _textPos < _textLen ? _text[_textPos] : char.MinValue;
        }

        private void NextChar()
        {
            if (_textPos < _textLen)
                ++_textPos;
            _ch = _textPos < _textLen ? _text[_textPos] : char.MinValue;
        }

        private void NextToken()
        {
            while (char.IsWhiteSpace(_ch))
                NextChar();
            int textPos = _textPos;
            TokenId tokenId;
            switch (_ch)
            {
                case '!':
                    NextChar();
                    if (_ch == 61)
                    {
                        NextChar();
                        tokenId = TokenId.ExclamationEqual;
                        break;
                    }
                    tokenId = TokenId.Exclamation;
                    break;
                case '"':
                case '\'':
                    char ch = _ch;
                    do
                    {
                        NextChar();
                        while (_textPos < _textLen && _ch != ch)
                            NextChar();
                        if (_textPos == _textLen)
                            throw ParseError(_textPos, "Unterminated string literal");
                        NextChar();
                    }
                    while (_ch == ch);
                    tokenId = TokenId.StringLiteral;
                    break;
                case '%':
                    NextChar();
                    tokenId = TokenId.Percent;
                    break;
                case '&':
                    NextChar();
                    if (_ch == 38)
                    {
                        NextChar();
                        tokenId = TokenId.DoubleAmphersand;
                        break;
                    }
                    tokenId = TokenId.Amphersand;
                    break;
                case '(':
                    NextChar();
                    tokenId = TokenId.OpenParen;
                    break;
                case ')':
                    NextChar();
                    tokenId = TokenId.CloseParen;
                    break;
                case '*':
                    NextChar();
                    tokenId = TokenId.Asterisk;
                    break;
                case '+':
                    NextChar();
                    tokenId = TokenId.Plus;
                    break;
                case ',':
                    NextChar();
                    tokenId = TokenId.Comma;
                    break;
                case '-':
                    NextChar();
                    tokenId = TokenId.Minus;
                    break;
                case '.':
                    NextChar();
                    tokenId = TokenId.Dot;
                    break;
                case '/':
                    NextChar();
                    tokenId = TokenId.Slash;
                    break;
                case ':':
                    NextChar();
                    tokenId = TokenId.Colon;
                    break;
                case '<':
                    NextChar();
                    if (_ch == 61)
                    {
                        NextChar();
                        tokenId = TokenId.LessThanEqual;
                        break;
                    }
                    if (_ch == 62)
                    {
                        NextChar();
                        tokenId = TokenId.LessGreater;
                        break;
                    }
                    tokenId = TokenId.LessThan;
                    break;
                case '=':
                    NextChar();
                    if (_ch == 61)
                    {
                        NextChar();
                        tokenId = TokenId.DoubleEqual;
                        break;
                    }
                    tokenId = TokenId.Equal;
                    break;
                case '>':
                    NextChar();
                    if (_ch == 61)
                    {
                        NextChar();
                        tokenId = TokenId.GreaterThanEqual;
                        break;
                    }
                    tokenId = TokenId.GreaterThan;
                    break;
                case '?':
                    NextChar();
                    tokenId = TokenId.Question;
                    break;
                case '[':
                    NextChar();
                    tokenId = TokenId.OpenBracket;
                    break;
                case ']':
                    NextChar();
                    tokenId = TokenId.CloseBracket;
                    break;
                case '|':
                    NextChar();
                    if (_ch == 124)
                    {
                        NextChar();
                        tokenId = TokenId.DoubleBar;
                        break;
                    }
                    tokenId = TokenId.Bar;
                    break;
                default:
                    if (char.IsLetter(_ch) || _ch == 64 || _ch == 95)
                    {
                        do
                        {
                            NextChar();
                        }
                        while (char.IsLetterOrDigit(_ch) || _ch == 95);
                        tokenId = TokenId.Identifier;
                        break;
                    }
                    if (char.IsDigit(_ch))
                    {
                        tokenId = TokenId.IntegerLiteral;
                        do
                        {
                            NextChar();
                        }
                        while (char.IsDigit(_ch));
                        if (_ch == 46)
                        {
                            tokenId = TokenId.RealLiteral;
                            NextChar();
                            ValidateDigit();
                            do
                            {
                                NextChar();
                            }
                            while (char.IsDigit(_ch));
                        }
                        if (_ch == 69 || _ch == 101)
                        {
                            tokenId = TokenId.RealLiteral;
                            NextChar();
                            if (_ch == 43 || _ch == 45)
                                NextChar();
                            ValidateDigit();
                            do
                            {
                                NextChar();
                            }
                            while (char.IsDigit(_ch));
                        }
                        if (_ch == 70 || _ch == 102)
                        {
                            NextChar();
                        }
                        break;
                    }
                    if (_textPos == _textLen)
                    {
                        tokenId = TokenId.End;
                        break;
                    }
                    throw ParseError(_textPos, "Syntax error '{0}'", (object)_ch);
            }
            _token.id = tokenId;
            _token.text = _text.Substring(textPos, _textPos - textPos);
            _token.pos = textPos;
        }

        private bool TokenIdentifierIs(string id)
        {
            if (_token.id == TokenId.Identifier)
                return string.Equals(id, _token.text, StringComparison.OrdinalIgnoreCase);
            return false;
        }

        private string GetIdentifier()
        {
            ValidateToken(TokenId.Identifier, "Identifier expected");
            string str = _token.text;
            if (str.Length > 1 && str[0] == 64)
                str = str.Substring(1);
            return str;
        }

        private void ValidateDigit()
        {
            if (!char.IsDigit(_ch))
                throw ParseError(_textPos, "Digit expected");
        }

        private void ValidateToken(TokenId t, string errorMessage)
        {
            if (_token.id != t)
                throw ParseError(errorMessage);
        }

        private void ValidateToken(TokenId t)
        {
            if (_token.id != t)
                throw ParseError("Syntax error");
        }

        private Exception ParseError(string format, params object[] args)
        {
            return ParseError(_token.pos, format, args);
        }

        private Exception ParseError(int pos, string format, params object[] args)
        {
            return new ParseException(string.Format(format, CultureInfo.CurrentCulture, args), pos);
        }

        private static Dictionary<string, object> CreateKeywords()
        {
            Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
            dictionary.Add("true", TrueLiteral);
            dictionary.Add("false", FalseLiteral);
            dictionary.Add("null", NullLiteral);
            dictionary.Add(KeywordIt, KeywordIt);
            dictionary.Add(KeywordOuterIt, KeywordOuterIt);
            dictionary.Add(KeywordIif, KeywordIif);
            dictionary.Add(KeywordNew, KeywordNew);
            foreach (Type predefinedType in PredefinedTypes)
                dictionary.Add(predefinedType.Name, predefinedType);
            return dictionary;
        }

        private struct Token
        {
            public TokenId id;
            public string text;
            public int pos;
        }

        private enum TokenId
        {
            Unknown,
            End,
            Identifier,
            StringLiteral,
            IntegerLiteral,
            RealLiteral,
            Exclamation,
            Percent,
            Amphersand,
            OpenParen,
            CloseParen,
            Asterisk,
            Plus,
            Comma,
            Minus,
            Dot,
            Slash,
            Colon,
            LessThan,
            Equal,
            GreaterThan,
            Question,
            OpenBracket,
            CloseBracket,
            Bar,
            ExclamationEqual,
            DoubleAmphersand,
            LessThanEqual,
            LessGreater,
            DoubleEqual,
            GreaterThanEqual,
            DoubleBar
        }

        private interface ILogicalSignatures
        {
            void F(bool x, bool y);

            void F(bool? x, bool? y);
        }

        private interface IArithmeticSignatures
        {
            void F(int x, int y);

            void F(uint x, uint y);

            void F(long x, long y);

            void F(ulong x, ulong y);

            void F(float x, float y);

            void F(double x, double y);

            void F(Decimal x, Decimal y);

            void F(int? x, int? y);

            void F(uint? x, uint? y);

            void F(long? x, long? y);

            void F(ulong? x, ulong? y);

            void F(float? x, float? y);

            void F(double? x, double? y);

            void F(Decimal? x, Decimal? y);
        }

        private interface IRelationalSignatures : IArithmeticSignatures
        {
            void F(string x, string y);

            void F(char x, char y);

            void F(DateTime x, DateTime y);

            void F(DateTimeOffset x, DateTimeOffset y);

            void F(TimeSpan x, TimeSpan y);

            void F(char? x, char? y);

            void F(DateTime? x, DateTime? y);

            void F(DateTimeOffset? x, DateTimeOffset? y);

            void F(TimeSpan? x, TimeSpan? y);
        }

        private interface IEqualitySignatures : IRelationalSignatures, IArithmeticSignatures
        {
            void F(bool x, bool y);

            void F(bool? x, bool? y);

            void F(Guid x, Guid y);

            void F(Guid? x, Guid? y);
        }

        private interface IAddSignatures : IArithmeticSignatures
        {
            void F(DateTime x, TimeSpan y);

            void F(TimeSpan x, TimeSpan y);

            void F(DateTime? x, TimeSpan? y);

            void F(TimeSpan? x, TimeSpan? y);
        }

        private interface ISubtractSignatures : IAddSignatures, IArithmeticSignatures
        {
            void F(DateTime x, DateTime y);

            void F(DateTime? x, DateTime? y);
        }

        private interface INegationSignatures
        {
            void F(int x);

            void F(long x);

            void F(float x);

            void F(double x);

            void F(Decimal x);

            void F(int? x);

            void F(long? x);

            void F(float? x);

            void F(double? x);

            void F(Decimal? x);
        }

        private interface INotSignatures
        {
            void F(bool x);

            void F(bool? x);
        }

        private interface IEnumerableSignatures
        {
            bool Contains(object selector);

            void Where(bool predicate);

            void Any();

            void Any(bool predicate);

            void All(bool predicate);

            void Count();

            void Count(bool predicate);

            void Min(object selector);

            void Max(object selector);

            void Sum(int selector);

            void Sum(int? selector);

            void Sum(long selector);

            void Sum(long? selector);

            void Sum(float selector);

            void Sum(float? selector);

            void Sum(double selector);

            void Sum(double? selector);

            void Sum(Decimal selector);

            void Sum(Decimal? selector);

            void Average(int selector);

            void Average(int? selector);

            void Average(long selector);

            void Average(long? selector);

            void Average(float selector);

            void Average(float? selector);

            void Average(double selector);

            void Average(double? selector);

            void Average(Decimal selector);

            void Average(Decimal? selector);
        }

        private class MethodData
        {
            public MethodBase MethodBase;
            public ParameterInfo[] Parameters;
            public Expression[] Args;
        }
    }
}
